package com.zp.datax.core;

import com.zp.datax.core.function.BiFunctionWrapper;
import com.zp.datax.core.function.ConsumerWrapper;
import com.zp.datax.core.function.SupplierWrapper;
import com.zp.datax.core.model.DataxJobLog;
import com.zp.datax.core.result.ReturnT;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 脚本处理器
 *
 * @author bruce
 * @date 2019-07-15
 */
@Slf4j
public abstract class ExecProcessor {

    private final static String COMMAND_PYTHON = "python";
    private final String COMMAND_PYTHON_SCRIPT;
    //job文件，绝对路径
    private final String pathname;
    //执行参数
    protected String strCondition;
    //job名称
    private final String jobname;
    //job日志存储目录
    private final String logdir;
    private String logname;
    protected ExecProcessor processor;

    protected ExecProcessor(String pathname, String strCondition) {
        this.pathname = pathname;
        this.strCondition = strCondition;

        int lastIndexOf = pathname.lastIndexOf(File.separator);
        this.jobname = pathname.substring(lastIndexOf + 1, pathname.length() - 5);
        this.logdir = pathname.substring(0, lastIndexOf)
                .replace("job", "log")
                .concat(File.separator)
                .concat(LocalDate.now(ZoneId.systemDefault()).toString());
        this.COMMAND_PYTHON_SCRIPT = pathname.substring(0, lastIndexOf)
                .replace("job", "bin")
                .concat(File.separator)
                .concat("datax.py");
    }

    protected ExecProcessor(ExecProcessor processor) {
        this(processor.pathname, processor.strCondition);
        this.processor = processor;
    }

    /**
     * 获取job日志路径
     *
     * @return
     */
    protected String getLastLogPath() {

        String logpath = null;

        File file = new File(logdir);
        if (!file.exists()) {
            log.warn("目录 :: {} 不存在", logdir);
            return logpath;
        }

        String regex = String.format("[a-zA-Z0-9_-]*%s\\_json[0-9_-]{9}\\.[0-9]{3}\\.log", jobname);

        File[] files = file.listFiles(((dir, name) -> name.matches(regex)));

        if (Objects.nonNull(files) && files.length > 0) {
            logpath = Arrays.stream(files).max(Comparator.comparing(File::lastModified))
                    .get()
                    .getPath();

            logname = logpath.substring(logpath.lastIndexOf(File.separator) + 1, logpath.length() - 4);

        }

        return logpath;
    }

    protected String getLogname() {
        return logname;
    }

    /**
     * 在单独的进程中执行指定的字符串命令
     *
     * @param command
     * @return
     */
    @SneakyThrows
    protected Process exec(String command) {
        log.info("执行命令 : {}", command);

        return Runtime.getRuntime().exec(command);
    }

    /**
     * 在单独的进程中执行指令的命令和变量
     *
     * @param cmdarray
     * @return
     */
    @SneakyThrows
    protected Process exec(String[] cmdarray) {
        log.info("执行命令 : {}", String.join(", ", cmdarray));

        return Runtime.getRuntime().exec(cmdarray);
    }

    /**
     * 在指定环境的独立进程中执行指定的命令和变量
     *
     * @param cmdarray
     * @param envp
     * @return
     */
    @SneakyThrows
    protected Process exec(String[] cmdarray, String[] envp) {
        log.info("执行命令 : {}, 执行环境 : {}"
                , String.join(", ", cmdarray)
                , String.join(", ", envp));

        return Runtime.getRuntime().exec(cmdarray, envp);
    }

    /**
     * 在指定环境和工作目录的独立进程中执行指定的命令和变量
     *
     * @param cmdarray
     * @param envp
     * @param dirname
     * @return
     */
    @SneakyThrows
    protected Process exec(String[] cmdarray, String[] envp, String dirname) {
        log.info("执行命令 : {}, 执行环境 : {}, 工作目录 : {}"
                , String.join(", ", cmdarray)
                , String.join(", ", envp)
                , dirname);

        return Runtime.getRuntime().exec(cmdarray, envp, new File(dirname));
    }

    @SneakyThrows
    protected ReturnT exec(BiFunctionWrapper<Reader, Process, ReturnT, Exception> function
            , String command) {
        return exec(function, () -> exec(command));
    }

    @SneakyThrows
    protected ReturnT exec(BiFunctionWrapper<Reader, Process, ReturnT, Exception> function
            , String[] cmdarray) {
        return exec(function, () -> exec(cmdarray));
    }

    @SneakyThrows
    protected ReturnT exec(BiFunctionWrapper<Reader, Process, ReturnT, Exception> function
            , String[] cmdarray
            , String[] envp) {
        return exec(function, () -> exec(cmdarray, envp));
    }

    @SneakyThrows
    protected ReturnT exec(BiFunctionWrapper<Reader, Process, ReturnT, Exception> function
            , String[] cmdarray
            , String[] envp
            , String dirname) {
        return exec(function, () -> exec(cmdarray, envp, dirname));
    }

    @SneakyThrows
    protected ReturnT exec(BiFunctionWrapper<Reader, Process, ReturnT, Exception> function
            , SupplierWrapper<Process, Exception> supplier) {

        @Cleanup("destroy")
        Process process = supplier.get();
        @Cleanup
        InputStream inputStream = process.getInputStream();
        @Cleanup
        Reader reader = new InputStreamReader(inputStream);
        @Cleanup
        BufferedReader bufferedReader = new BufferedReader(reader);

        return function.apply(bufferedReader, process);
    }

    /**
     * 抽象方法，交给子类实现
     *
     * @param handler
     * @return
     * @throws IllegalArgumentException
     */
    protected abstract ReturnT exec(Handler handler, ConsumerWrapper<DataxJobLog, Exception> consumerWrapper, DataxJobLog dataxJobLog);

    /**
     * 拼装执行变量
     *
     * @return
     */
    private String appendVariable() {

        String[] array = strCondition.split(",");
        String appAfterVariable = Arrays.stream(array)
                .map(s -> "-D".concat(s.trim()))
                .collect(Collectors.joining(" "));

        return String.format("\"%s\"", appAfterVariable);
    }

    /**
     * 获取基本命令
     *
     * @return
     */
    protected String[] getBasicCommand() {

        return new String[]
                {
                        COMMAND_PYTHON
                        , COMMAND_PYTHON_SCRIPT
                        , pathname
                };

    }

    /**
     * 获取执行命令和变量
     *
     * @return
     */
    protected String[] getCommandArray() {

        String[] cmdarray = getBasicCommand();

        if (Objects.nonNull(strCondition)
                && !"".equals(strCondition.trim())
                && strCondition.length() > 0) {

            String[] newArray = Arrays.copyOf(cmdarray, 5);
            newArray[3] = "-p";
            newArray[4] = appendVariable();
            return newArray;
        }

        return cmdarray;
    }

    /**
     * 设置参数
     *
     * @param strCondition
     */
    public void setStrCondition(String strCondition) {
        this.strCondition = strCondition;
    }

}
